#ifndef __DANIEL_TCPSOCKET_H__
#define __DANIEL_TCPSOCKET_H__

#include <sys/socket.h>
#include <memory>
#include <string>
#include <config.h>
//客户端用TCP通信类封装， socket()->connect()  send()   recv()
//同时兼容服务器，可使用完成其消息收发功能
namespace daniel
{
    class TcpSocket
    {
    public:
        using ptr = std::shared_ptr<TcpSocket>;
        TcpSocket();
        TcpSocket(int fd);

        ~TcpSocket();
        //连接服务端，成功返回true，失败返回false
        void connectToHost(const std::string &ip, uint16_t port);
        //断开和服务器的连接
        void disconnect();
        //返回接收到的字符串
        void recvMsg(std::string& recvMsg);
        //发送消息
        void sendMsg(const std::string &msg);

    private:
        TcpSocket(const TcpSocket &) = delete;
        TcpSocket operator=(const TcpSocket &) = delete;
        enum EventType{ ReadEvent=0, WriteEvent=1};
        //设置阻塞和非阻塞
        void setNonblock(int fd);
        void setBlock(int fd);
        //检测是否可读、可写，设置超时   单位为ms
        int32_t isFDReady(int32_t fd, int32_t timeout,const EventType& type);
        int32_t isReadyRead(int32_t fd,int32_t timeout);
        int32_t isReadyWrite(int32_t fd,int32_t timeout);
        int32_t isConnectReady(int fd, struct sockaddr* addr, int length, int32_t timeout);
        //一次性写入N个字节，或者一次性读入N个字节
        int32_t readNByte(char* dest, int32_t n);
        int32_t writeNByte( char* src, int32_t n);
        int32_t rdWrNByte(char* buf, int32_t n, const EventType& type);
        
    private:
        char m_buf[4096]={'\0'};
        //文件描述符和缓冲区
        int m_connectfd;
    };
    struct HostInfo
    {
        HostInfo(){}
        HostInfo(const std::string& ip, uint16_t port):m_ip(ip),m_port(port){}
        std::string m_ip;
        uint16_t m_port;
    };
    template <>
    class LexicalCast<std::string, HostInfo>
    {
    public:
        HostInfo operator()(const std::string& str)
        {
            HostInfo retInfo;
            YAML::Node root=YAML::Load(str);
            retInfo.m_ip=root["ip"].as<std::string>();
            retInfo.m_port=root["port"].as<uint16_t>();
            return retInfo;
        }
    };
    template<>
    class LexicalCast<HostInfo, std::string>
    {
    public:
        std::string operator() (const HostInfo& hostInfo)
        {
            YAML::Node root;
            YAML::Node child;
            child["ip"]=hostInfo.m_ip;
            child["port"]=hostInfo.m_port;
            root["hostinfo"]=child;
            std::stringstream ss;
            ss<<root;
            return ss.str();
        }
    };
}

#endif